﻿using Mad.Core.DomainServices;
using Mad.Core.Model;
using Mad.Infrastructure.ApplicationServices;
using Mad.Infrastructure.DomainServices;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Mad.DataMigrationEngine
{
    /// <summary>
    /// This is a sample Using SqlBulkCopy for high performance inserts with C#. 
    /// If you have any questions let me know, my email: bin.nguyen.van@gmail.com
    /// </summary>
    public partial class SqlBulkCopyForm : Form
    {
        private readonly IAppConfig appConfig;
        private delegate void UpdateProcessBar(string pTableName);
        private delegate void ValidConnection();
        private readonly ITranferService tranfer;
        private readonly ILoggerService logger;

        public SqlBulkCopyForm()
        {
            InitializeComponent();
            this.backgroundWorker.WorkerReportsProgress = true;
            this.backgroundWorker.WorkerSupportsCancellation = true;

            this.backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker_DoWork);
            this.backgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
            this.backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;

            txtSourceServer.Validating += txtSourceServer_Validating;
            txtSourceDatabase.Validating += txtSourceDatabase_Validating; 

            txtDestinationServer.Validating += txtDestinationServer_Validating;
            txtDestinationDatabase.Validating += txtDestinationDatabase_Validating; 

            appConfig = new AppConfig();
            SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(appConfig.DataSourceConnection);
            txtSourceServer.Text = builder.DataSource;
            txtSourceDatabase.Text = builder.InitialCatalog;
            txtSourceLogin.Text = builder.UserID;
            txtSourcePassword.Text = builder.Password;

            builder = new SqlConnectionStringBuilder(appConfig.DestinationConnection);
            txtDestinationServer.Text = builder.DataSource;
            txtDestinationDatabase.Text = builder.InitialCatalog;
            txtDestinationLogin.Text = builder.UserID;
            txtDestinationPassword.Text = builder.Password;
            tranfer = new TranferService(appConfig);
            logger = new FileLoggerService();
        }
        
        void txtDestinationDatabase_Validating(object sender, CancelEventArgs e)
        {
            if (txtDestinationDatabase.Text == "") errorProvider.SetError(txtDestinationDatabase, Constants.Message.REQUIRE_FIELD);
            else errorProvider.SetError(txtDestinationDatabase, "");
        }

        void txtDestinationServer_Validating(object sender, CancelEventArgs e)
        {
            if (txtDestinationServer.Text == "") errorProvider.SetError(txtDestinationServer, Constants.Message.REQUIRE_FIELD);
            else errorProvider.SetError(txtDestinationServer, "");
        }

        void txtSourceDatabase_Validating(object sender, CancelEventArgs e)
        {
            if (txtSourceDatabase.Text == "") errorProvider.SetError(txtSourceDatabase, Constants.Message.REQUIRE_FIELD);
            else errorProvider.SetError(txtSourceDatabase, "");
        }

        void txtSourceServer_Validating(object sender, CancelEventArgs e)
        {
            if (txtSourceServer.Text == "") errorProvider.SetError(txtSourceServer, Constants.Message.REQUIRE_FIELD);
            else errorProvider.SetError(txtSourceServer, "");
        }

        private void UpdateProcessStatus(string pTableName)
        {
            lblProcessValue.Text = pTableName;
        }

        private void btnInclude_Click(object sender, EventArgs e)
        {
            foreach (object item in listTablesSource.SelectedItems)
            {
                listTablesSelected.Items.Add(item);
            }
            ListBox.SelectedIndexCollection indices = listTablesSource.SelectedIndices;
            for (int i = indices.Count - 1; i >= 0; i--)
            {
                listTablesSource.Items.RemoveAt(indices[i]);
            }
        }

        private void btnIncludeAll_Click(object sender, EventArgs e)
        {
            foreach (object item in listTablesSource.Items)
            {
                listTablesSelected.Items.Add(item);
            }

            listTablesSource.Items.Clear();
        }

        private void btnExclude_Click(object sender, EventArgs e)
        {
            foreach (object item in listTablesSelected.SelectedItems)
            {
                listTablesSource.Items.Add(item);
            }
            ListBox.SelectedIndexCollection indices = listTablesSelected.SelectedIndices;
            for (int i = indices.Count - 1; i >= 0; i--)
            {
                listTablesSelected.Items.RemoveAt(indices[i]);
            }
        }

        private void btnExcludeAll_Click(object sender, EventArgs e)
        {
            foreach (object item in listTablesSelected.Items)
            {
                listTablesSource.Items.Add(item);
            }

            listTablesSelected.Items.Clear();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            List<ProviderInfo> providers = new List<ProviderInfo>();
            ProviderInfo srcProvider = new ProviderInfo
            {
                InitialCatalog = txtSourceDatabase.Text,
                Server = txtSourceServer.Text,
                UserID = txtSourceLogin.Text,
                Password = txtSourcePassword.Text
            };

            ProviderInfo destProvider = new ProviderInfo
            {
                InitialCatalog = txtDestinationDatabase.Text,
                Server = txtDestinationServer.Text,
                UserID = txtDestinationLogin.Text,
                Password = txtDestinationPassword.Text
            };

            providers.Add(srcProvider);
            providers.Add(destProvider);

            lblProcessValue.Text = string.Empty;
            btnStart.Enabled = false;
            btnViewLog.Visible = false;
            progressBar.Visible = true;
            progressBar.Value = 0;
            progressBar.Maximum = listTablesSelected.Items.Count;
            backgroundWorker.RunWorkerAsync(providers);
        }

        private void SqlBulkCopyForm_Load(object sender, EventArgs e)
        {            
            var repository = new TableRepository(appConfig);
            ProviderInfo srcProvider = new ProviderInfo
            {
                InitialCatalog = txtSourceDatabase.Text,
                Server = txtSourceServer.Text,
                UserID = txtSourceLogin.Text,
                Password = txtSourcePassword.Text
            };
            var ds = repository.GetDataByQuery(srcProvider, Constants.Table.QUERY_ALL_TABLE_SCHEMA);

            foreach (DataRow dataRow in ds.Tables[0].Rows)
            {
                var tableItem = new TableItem(dataRow);
                listTablesSource.Items.Add(tableItem.Item);
            }           
        }

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            List<string> logs = new List<string>();
            try
            {
                var providers = (List<ProviderInfo>)e.Argument;
                var srcConnection = tranfer.ConnectState(providers.FirstOrDefault());

                if (srcConnection.HasError)
                {
                    MessageBox.Show( this, "[Source server]=>"+srcConnection.Response.ToString());
                    e.Cancel = true;
                    progressBar.Visible = false;
                    return;
                }

                var destConnection = tranfer.ConnectState(providers.LastOrDefault());
                if (destConnection.HasError)
                {
                    MessageBox.Show(this, "[Destination server]=>" + destConnection.Response.ToString());
                    e.Cancel = true;
                    progressBar.Visible = false;
                    return;
                }

                if (listTablesSelected.Items == null || listTablesSelected.Items.Count == 0)
                {
                    MessageBox.Show(this, "Please select at least one item to tranfer!");
                    e.Cancel = true;
                    progressBar.Visible = false;
                    return;
                }
                UpdateProcessBar process = new UpdateProcessBar(UpdateProcessStatus);

                foreach (var table in listTablesSelected.Items)
                {
                    if (backgroundWorker.CancellationPending)
                        e.Cancel = true;
                    else
                    {
                        string tableName = "=>" + table.ToString();
                        logs.Add(tableName);
                        this.Invoke(process, new object[] { tableName });
                        var response = tranfer.MultipleBulkCopy(providers.FirstOrDefault(), providers.LastOrDefault(), table.ToString());
                        if (response.HasError) logs.Add(response.Response.ToString());
                        backgroundWorker.ReportProgress(0, table.ToString());
                    }
                }
            }
            catch(Exception ex) {
                logs.Add(ex.Message);
            }

            logger.LoggerInfo(logs);
        }
        
        void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (!e.Cancelled)
            {
                lblProcessValue.Text = "Completed";
                btnViewLog.Visible = true;
                MessageBox.Show(this, "Your work have been done!");
            }
            btnStart.Enabled = true;

        }

        void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            string strTable = (string)e.UserState;
            progressBar.Value++;
        }

        private void btnViewLog_Click(object sender, EventArgs e)
        {
            logger.OpenLogger();
        }

    }
}
